<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>五子棋</title>
    <style>
        /*不能在里面设置宽高*/
        canvas {
            background-color: green;
        }
    </style>
</head>
<body>
<button id="bt">绘画除棋盘</button>
<span id="tip"></span>
<br>
<br>

</body>

<script>

    const tipInf = (t) => {
        document.getElementById('tip').textContent = t;
    }


    const canvas = document.createElement('canvas');
    document.body.append(canvas)
    const ctx = canvas.getContext('2d');
    let width = 600;
    let height = 600;
    canvas.width = width
    canvas.height = height


    let curColor = 'black';
    tipInf('当前黑棋落子！');

    //上下左右,30
    let margin = 30;
    //棋盘的高度，宽度
    let groundWidth = 540;
    let groundHeight = 540;

    //棋盘的行列
    let row = 15, cow = 15, dr = groundHeight / row, dc = groundWidth / cow;

    //使用二维数组，记录哪些位置已经被放置了棋子，防止重复放置
    let mat = [];
    for (let i = 0; i <= row; i++) {
        mat[i] = [];
        for (let j = 0; j <= cow; j++) {
            mat[i][j] = 0;
        }
    }
    const generateGround = () => {
        //绘制行
        for (let i = 0; i <= row; i++) {
            ctx.beginPath()
            ctx.lineTo(margin, margin + i * dr);
            ctx.lineTo(margin + groundWidth, margin + i * dr);
            ctx.stroke();
            ctx.closePath()
        }
        //绘制列
        for (let i = 0; i <= cow; i++) {
            ctx.beginPath()
            ctx.lineTo(margin + i * dc, margin);
            ctx.lineTo(margin + i * dc, margin + groundHeight);
            ctx.stroke();
            ctx.closePath()
        }


        //给canvas添加点击事件
        canvas.addEventListener('click', e => {
            console.log(e);
            let x = e.offsetX, y = e.offsetY;
            if (x < margin || y < margin || x > margin + groundWidth || y > margin + groundHeight) {
                return;
            }
            //实现画圆画到交叉处
            let m1 = (x - margin) % dc, m2 = (y - margin) % dr;
            let c = x, r = y;
            // if (m1 <= (dc / 2)) {
            //     c = x - m1;
            // } else {
            //     c = x - m1 + dc;
            // }
            // if (m2 <= (dr / 2)) {
            //     r = y - m2;
            // } else {
            //     r = y - m2 + dr;
            // }

            //实现画圆画到交叉处 方法二 将长度加上格子一半，然后向下取整
            //nthC,nthR表示是第几行第几列
            const nthC = Math.floor((x - margin + (dc / 2)) / dc);
            const nthR = Math.floor((y - margin + (dr / 2)) / dr);
            if (mat[nthR][nthC] === 0) {
                c = nthC * dc + margin;
                r = nthR * dr + margin;
                generateBall(c, r, 15, curColor);
                //黑色是1白色是2
                mat[nthR][nthC] = curColor === 'black' ? 1 : 2

                //判断是否获胜
                if (isWin(nthR,nthC)) {
                    tipInf((curColor === 'black' ? '黑棋' : '白棋') + '赢了！');
                    return;
                }
                //颜色要变换
                curColor = (curColor === 'black' ? 'white' : 'black');
                tipInf('当前' + (curColor === 'black' ? '黑棋' : '白棋') + '落子！');
            } else {
                tipInf('此处已有棋子，请重新落子，当前' + (curColor === 'black' ? '黑棋' : '白棋') + '落子！');
            }

        });

    }


    const generateBall = (x, y, r, color) => {
        ctx.beginPath()
        ctx.arc(x, y, r, 0, deg2Arc(360));
        //黑色圆形渐变都是左边在x,y都减去r/2,第一个r为0，第2个r就1.5r
        //白色圆形渐变都是左边在x,y都加上r/2,第一个r为0，第2个r就1.5r
        let dx = color === 'black' ? x - (r / 2) : x + (r / 2);
        let dy = color === 'black' ? y - (r / 2) : y + (r / 2);
        const gradient = ctx.createRadialGradient(dx, dy, 0, dx, dy, 1.5 * r);
        gradient.addColorStop(0, color === 'black' ? 'white' : 'gray');
        gradient.addColorStop(1, color === 'black' ? 'black' : 'white');

        ctx.shadowBlur = 4;
        ctx.shadowOffsetX = 4;
        ctx.shadowOffsetY = 4;
        ctx.shadowColor = '#666';

        ctx.fillStyle = gradient;
        ctx.fill();
        ctx.closePath()
    }

    const button = document.getElementById('bt');
    button.addEventListener('click', generateGround);


    const deg2Arc = (deg) => {
        return (deg * Math.PI) / 180;
    }

    /**
     * 判断r行c列是否赢了
     */
    const isWin = (r, c) => {
        let color = mat[r][c];

        //先判断竖直方向是否五子
        let cnt = 0;
        for (let i = r; i <= row; i++) {
            if (mat[i][c] !== color) {
                break;
            }
            cnt++;
        }
        for (let i = r-1; i >=0; i--) {
            if (mat[i][c] !== color) {
                break;
            }
            cnt++;
        }
        if (cnt === 5) {
            return true;
        }
        //判断水平方向是否五子
        cnt = 0;
        for (let i = c; i <= cow; i++) {
            if (mat[r][i] !== color) {
                break;
            }
            cnt++;
        }
        for (let i = c - 1; i >=0; i--) {
            if (mat[r][i] !== color) {
                break;
            }
            cnt++;
        }
        if (cnt === 5) {
            return true;
        }

        //对角线1  /
        cnt=0;
        let i = r, j = c;
        while (i >= 0 && j <= cow && mat[i][j] === color) {
            cnt++;
            i--;
            j++;
        }
        i = r + 1, j = c - 1;
        while (i <= row && j >= 0 && mat[i][j] === color) {
            cnt++;
            i++;
            j--;
        }
        if (cnt === 5) {
            return true;
        }

        //对角线2  \
        cnt=0;
        i = r, j = c;
        while (i <= row && j <= cow && mat[i][j] === color) {
            cnt++;
            i++;
            j++;
        }
        i = r - 1, j = c - 1;
        while (i >= 0 && j >= 0 && mat[i][j] === color) {
            cnt++;
            i--;
            j--;
        }
        if (cnt === 5) {
            return true;
        }
    }
</script>
</html>